import os, sys, unittest

import dt

INDENTS = ["\t", ' ']
IN = True
NOT_IN = False
def next(in_or_not, line, chars, offset):
    while True:
        c = line[offset]
        result = None
        if in_or_not:
            result = c in chars
        else:
            result = c not in chars
        if result:
            break
        offset = offset + 1
        if offset >= len(line):
            return -1
    return offset

def push_stack(stack, index, obj):
    padding = index + 1 - len(stack)
    if padding > 0:
        for i in range(0, padding):
            stack.append(None)
    stack[index] = obj

def read_str(line, offset):
    offset = next(NOT_IN, line, INDENTS, 0)
    start = offset
    if offset == -1:
        return None
    c = line[offset]
    retn = None
    if c == '"' or c == "'": # string is quoted
        while True:
            offset = offset + 1
            if offset >= len(line):
                offset = -1
                break
            foo = line[offset]
            if foo == '\\': # escape character
                offset = offset + 1
                continue
            if foo == c:
                break
        if offset == -1: 
            # line is not correctly ended
            return None
        retn = eval(line[start : offset + 1])
    else:
        i = next(IN, line, INDENTS, offset)
        if i == -1:
            offset = len(line) - 1 # end of line
        else:
            offset = i - 1 # tab or space should excluded
        retn = line[start : offset + 1]
    return [start, offset, retn]

def read_data_file(fname):
    if not os.path.isfile(fname):
        return None
    # file read to end
    fd = open(fname)
    lines = fd.read()
    fd.close()
    lines = lines.split('\n')
    # descrialize file
    retn = []
    stack = []
    indent = None # what indent does this file is using
    line_number = 0
    for line in lines:
        line_number = line_number + 1
        if line is None or len(line) == 0 or line.startswith('#'): 
            # skip empty line, comment line
            continue
        r = read_str(line, 0)
        if r is None: # this line is ...
            # print("skiped line no = %d %s" % (line_number, line))
            continue
        start = r[0]
        end = r[1]
        key = r[2]
        # calc indent level
        indent_level = None
        if start == 0:
            indent_level = 0
        else:
            if indent is None:
                indent = line[:start]
                print("Using indent: #-%s-#" % indent)
                indent_level = 1
            else:
                indent_level = int(start / len(indent))
                if not indent * indent_level == line[:start]:
                    print("Invalid indent at line %d" %  line_number)
                    return
        val = line[end + 1:]
        node = dt.Node(key, val)
        parent = None
        if indent_level == 0:
            parent = retn
        else:
            parent = stack[indent_level - 1]
        if parent is None:
            print("Parent not found, line %d" % line_number)
            return
        # append child node
        parent.append(node)
        # set stack
        push_stack(stack, indent_level, node)
    return retn

class TestClass(unittest.TestCase):

    def test_read_str(self):
        r = read_str("foo bar", 0)
        self.assertFalse(r is None)
        self.assertTrue(r[0] == 0 and r[1] == 2)

        r = read_str("'foo' bar", 0)
        self.assertTrue(r[0] == 0 and r[1] == 4)

        r = read_str("foo\" bar", 0)
        self.assertTrue(r[0] == 0 and r[1] == 3)

        r = read_str("  fo\"o bar", 0)
        self.assertTrue(r[0] == 2 and r[1] == 5)

def print_node(node, indent):
    print('%s%s = %s' % ('  ' * indent, node.Key, node.Value))
    for child in node:
        print_node(child, indent + 1)
        
if __name__ == "__main__":
    if len(sys.argv) > 1:
        if sys.argv[1] == "test":
            unittest.main()
    else:
        fname = "data/ships.txt"
        ships = read_data_file(fname)
        print(len(ships))
        for ship in ships:
            print_node(ship, 0)
